<?php

/*
 * Читает сайт и сохраняет в базу
 * 1. откуда читать знает ParseInfo
 * 2. надо знать как переходить от одной статьи к следующей
 * 2.1 надо знать когда мы получиили не то, что хотели
 * 3. как распарсить статью знает Parser
 * 4. как сохранить статью знает модель
 */

/**
 * Description of DataLoader
 *
 * @author pahhan
 */
abstract class Model_DataLoader
{
    /**
     * @var Model_ParseInfo
     */
    private $parse_info;

    /**
     * @var Model_Parser
     */
    private $parser;

    /**
     *
     * @var array of Model_Definer
     */
    private $definers = array();

    public function addDefiner(Model_Definer $definer)
    {
        $this->definers[] = $definer;
    }

    public function getHtml()
    {
        foreach($this->definers as $definer)
        {
            $url = $definer->getUrl();
            $html = $this->readUrl($url);
            if( $this->getParser()->validateHtml($html, $definer->getId()) )
            {
                return $html;
            }
            $this->parser->clearParseResult();
        }

        return FALSE;
    }

    public function load()
    {
        $html = $this->getHtml();
        if( $html === FALSE ) return FALSE;
        $result = $this->getParser()->parseHtml($html);
        $this->getParser()->clearParseResult();

        return $this->saveResult($result);
    }

    /**
     * Returns resource charset
     * @return string
     */
    public function getCharset()
    {
        return 'windows-1251';
    }

    /**
     * Returns url for loading next message
     * @param string $id Next external id
     * @return string
     */
    public function getNextUrl($id)
    {
        return $this->parse_info->getCompleteUrl($id);
    }

    /**
     * Returns next external id for
     * @param string $id Last message external id for parser
     */
    abstract public function getNextId($id);

    /**
     * Retirns parser oject. Factory method.
     * @return Model_Parser
     */
    public function getParser()
    {
        if(is_null($this->parser) )
        {
            $factory = new Model_Parser_ParserFactory();
            $this->parser = $factory->create($this->parse_info->getClassKey());
        }

        return $this->parser;
    }

    /**
     *
     * @return Model_ParseInfo
     */
    public function getParseInfo()
    {
        return $this->parse_info;
    }


    /**
     * Converts parser result to message object
     * @param Model_Parser_ParserResult $result
     * @return Model_Message
     */
    protected function resultToMessage(Model_Parser_ParserResult $result)
    {
        $message = new Model_Message();
        $message->setExternalId($result->getExternalId())
            ->setTitle($result->getTitle())
            ->setPublishedAt($result->getPublishedAt())
            ->setContent($result->getContent())
            ->setTags($result->getTags());

        return $message;

    }

    public function __construct(Model_ParseInfo $parse_info)
    {
        $this->parse_info = $parse_info;
    }

    /**
     *
     * @param string $url
     * @return string
     */
    public function readUrl($url)
    {
        $html = file_get_contents($url);
        $html = mb_convert_encoding($html, 'HTML-ENTITIES', $this->getCharset());
        return $html;
    }

    public function getParserNextUrl()
    {
        $parser = $this->getParser();
        $last_id = $this->parse_info->getLastExternalId();
        $url = $this->getNextUrl($last_id);
        $html = $this->getHtml($url);
        $next_url = $parser->getNextUrl($html);
        if( !$next_url ) return FALSE;

        if( substr($next_url, 0, 4) !== 'http' )
        {
            if( substr($next_url, 0, 1) === '/' )
            {
                $site = ORM::factory('Site', $this->parse_info->site_id);
                $next_url = rtrim($site->url, '/').$next_url;
            }
            else
            {
                $next_url = rtrim($url, '/').$next_url;
            }
        }

        return $next_url;
    }

    public function getReserveNextUrl()
    {
        return FALSE;
    }

    /**
     * Saves loaded message to database.
     * Returns external id of loaded message or false.
     * @return string|boolean
     */
    public function _load()
    {
        $parser = $this->getParser();

        $next_id = $this->getNextId($this->parse_info->getLastExternalId());
        $url = $this->getNextUrl($next_id);
        $html = $this->getHtml($url);
        $valid = $parser->validateHtml($html, $next_id);

        if( !$valid )
        {
            $parser->clearParseResult();
            $url = $this->getParserNextUrl();
            if( !$url )
            {
                $url = $this->getReserveNextUrl();
                if( !$url ) return FALSE;
                $valid = $parser->validateHtml($html, $next_id);
            }
            $html = $this->getHtml($url);
        }

        $result = $parser->parseHtml($html);
        $parser->clearParseResult();

        return $this->saveResult($result);

    }

    protected function saveResult(Model_Parser_ParserResult $result)
    {
        $message = $this->resultToMessage($result);
        $message->setParseInfoId($this->parse_info->getId())
                ->setSize($message->calculateSize());

        $db = Database::instance();

        $db->begin();

        try
        {
            $message->save();
            $this->parse_info->setLastExternalId($message->getExternalId())
                             ->setLastMessageId($message->getId())
                             ->setLastUpdate(date('Y-m-d H:i:s'))
                             ->save();
            $db->commit();
        }
        catch(Database_Exception $e)
        {
            $db->rollback();
            throw $e;
        }

        return $message->getExternalId();
    }


}

